#!/bin/csh -f

####################################################################################
## Copyright (c) 2014, University of British Columbia (UBC)  All rights reserved. ##
##                                                                                ##
## Redistribution  and  use  in  source   and  binary  forms,   with  or  without ##
## modification,  are permitted  provided that  the following conditions are met: ##
##   * Redistributions   of  source   code  must  retain   the   above  copyright ##
##     notice,  this   list   of   conditions   and   the  following  disclaimer. ##
##   * Redistributions  in  binary  form  must  reproduce  the  above   copyright ##
##     notice, this  list  of  conditions  and the  following  disclaimer in  the ##
##     documentation and/or  other  materials  provided  with  the  distribution. ##
##   * Neither the name of the University of British Columbia (UBC) nor the names ##
##     of   its   contributors  may  be  used  to  endorse  or   promote products ##
##     derived from  this  software without  specific  prior  written permission. ##
##                                                                                ##
## THIS  SOFTWARE IS  PROVIDED  BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ##
## AND  ANY EXPRESS  OR IMPLIED WARRANTIES,  INCLUDING,  BUT NOT LIMITED TO,  THE ##
## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ##
## DISCLAIMED.  IN NO  EVENT SHALL University of British Columbia (UBC) BE LIABLE ##
## FOR ANY DIRECT,  INDIRECT,  INCIDENTAL,  SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL ##
## DAMAGES  (INCLUDING,  BUT NOT LIMITED TO,  PROCUREMENT OF  SUBSTITUTE GOODS OR ##
## SERVICES;  LOSS OF USE,  DATA,  OR PROFITS;  OR BUSINESS INTERRUPTION) HOWEVER ##
## CAUSED AND ON ANY THEORY OF LIABILITY,  WHETHER IN CONTRACT, STRICT LIABILITY, ##
## OR TORT  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ##
## OF  THIS SOFTWARE,  EVEN  IF  ADVISED  OF  THE  POSSIBILITY  OF  SUCH  DAMAGE. ##
####################################################################################

####################################################################################
##                      Run-in-batch Synthesis Flow Manager                       ##
##                                                                                ##
##    Author: Ameer M. Abdelhadi (ameer@ece.ubc.ca, ameer.abdelhadi@gmail.com)    ##
## Switchable SRAM-based Multi-ported RAMs; University of British Columbia, 2014  ##
####################################################################################

####################################################################################
## USAGE:                                                                         ##
##    ./syn <Architecture List> <Bypass List> <Depth List> <Width List>   \       ##
##          <#Write Ports List> <#Read Ports List>                                ##
##                                                                                ##
## - Use a comma delimited list.                                                  ##
##   - No spaces.                                                                 ##
##   - May be surrounded by any brackets (), [], {}, or <>.                       ##
## - RAM depth and data width are positive integers.                              ##
## - Numbers of read and write ports are pairs of normal-extended port numbers    ##
##   delimited with hyphen "-".                                                   ##
##   - numbers of normal read/write ports are positive integers.                  ##
##   - 0                   <= # Extended write ports <=     # normal write ports  ##
##   - # normal read ports <= # Extended read  ports <= 2 * # noraml read  ports  ##
## - Architecture is one of: REG, XOR, LVTREG, LVTBIN, or LVT1HT.                 ##
##   - REG   : Register-based multi-ported RAM                                    ##
##   - XOR   : XOR-based multi-ported RAM                                         ##
##   - LVTREG: Register-based LVT multi-ported RAM                                ##
##   - LVTBIN: Binary-coded I-LVT-based multi-ported RAM                          ##
##   - LVT1HT: Onehot-coded I-LVT-based multi-ported RAM                          ##
## -Bypass list is one of: NON, WAW, RAW, or RDW.                                 ##
##   - WAW: Allow Write-After-Write (need to bypass feedback RAM)                 ##
##   - RAW: new data for Read-after-Write (need to bypass output RAM)             ##
##   - RDW: new data for Read-During-Write                                        ##
##                                                                                ##
## EXAMPLES:                                                                      ##
## ./syn XOR NON 1024 32 3-2 2-4                                                  ##
##    Synthesis a XOR-based RAM with no bypassing; 1K lines RAM; 32 bits width;   ##
##    3 normal / 2 extended write & 2 normal / 4 extended read ports.             ##
## ./syn LVTBIN,LVT1HT RAW,RDW 512,1024 8,16,32 2,3,4 1,2,3,4                     ##
##    Synthesis LVTBIN and LVT1HT RAMs with new data RAW and RDW bypassing; 512   ##
##    and 1024 lines; 8,16, and 32 data width; 2,3, and 4 write ports; 1,2,3, and ## 
##    4 read ports. Total of 144 RAM combinations.                                ##
##                                                                                ##
## The following files and directories will be created after compilation:         ##
##   - syn.res : A list of results, each run in a separate line, including:       ##
##               frequency, resources usage, and runtime                          ##
##   - log/    : Altera's logs and reports                                        ##
####################################################################################

# setup environment variables and Altera's CAD tools 
# change file to your own flow if necessary 
source ./altera.14.0.csh

# require exactly 6 arguments
if (${#argv} != 6) then
    printf '\x1b[%i;3%im' 1 1
    printf 'Error: Exactly 6 arguments are required\n'
    printf '\x1b[0m'
    goto errorMessage
endif

# convert each argument list into a c-shell list (romove commas and etc.)
set ARLST = (`echo ${argv[1]} | tr ",()[]{}<>" " "`)
set BYLST = (`echo ${argv[2]} | tr ",()[]{}<>" " "`)
set RDLST = (`echo ${argv[3]} | tr ",()[]{}<>" " "`)
set DWLST = (`echo ${argv[4]} | tr ",()[]{}<>" " "`)
set NWLST = (`echo ${argv[5]} | tr ",()[]{}<>" " "`)
set NRLST = (`echo ${argv[6]} | tr ",()[]{}<>" " "`)

# check arguments correctness (positive integer numbers)
foreach INTVAL ($RDLST $DWLST)
  set INTVALIsNumber=`echo $INTVAL | egrep -c '^[0-9]+$'`
  if ($INTVALIsNumber != 1) then
    printf '\x1b[%i;3%im' 1 1
    printf "Error (${INTVAL}): Depth and width arguments should be possitive integer numbers\n"
    printf '\x1b[0m'
    goto errorMessage
  endif
end

# check architicture list argument correctness
foreach ARVAL ($ARLST)
  if ( ($ARVAL != "REG") & ($ARVAL != "XOR") & ($ARVAL != "LVTREG") & ($ARVAL != "LVTBIN") & ($ARVAL != "LVT1HT") ) then
    printf '\x1b[%i;3%im' 1 1
    printf "Error (${ARVAL}): Architicture list should be a list of REG, XOR, LVTREG, LVTBIN, or LVT1HT\b\n"
    printf '\x1b[0m'
    goto errorMessage
  endif
end

# check bypass list argument correctness
foreach BYVAL ($BYLST)
  if ( ($BYVAL != "NON") & ($BYVAL != "WAW") & ($BYVAL != "RAW") & ($BYVAL != "RDW") ) then
    printf '\x1b[%i;3%im' 1 1
    printf "Error (${BYVAL}): Bypass list should be a list of NON, WAW, RAW, or RDW\b\n"
    printf '\x1b[0m'
    goto errorMessage
  endif
end

# check arguments correctness (positive integer numbers)
foreach ARGVAL ($NWLST $NRLST)
  set ARGVALIsNumber_=`echo $ARGVAL | egrep -c '^[0-9]+(\-[0-9])?[0-9]*$'`
  if ($ARGVALIsNumber_ != 1) then
    printf '\x1b[%i;3%im' 1 1
    printf 'Error: The number of reading and writing ports should be possitive integer numbers\n'
    printf '       Use "-" to seperate normal from extended mode parameters\n'
    printf '\x1b[0m'
    goto errorMessage
  endif
end

# total different fifo designs
@ FlowOprNum = ((${#RDLST})*(${#DWLST})*(${#NWLST})*(${#NRLST})*(${#ARLST})*(${#BYLST}))
@ FlowOprCnt = 0

printf '\x1b[%i;3%im' 7 4
printf "= Synthesis in batch with the following parameters:\n"
printf "= RAM Depth    : $RDLST\n"
printf "= Data Width   : $DWLST\n"
printf "= Write Ports  : $NWLST\n"
printf "= Read  Ports  : $NRLST\n"
printf "= Architicture : $ARLST\n"
printf "= Bypass       : $BYLST\n"
printf '\x1b[0m'

#print header
set FML  = `grep " FAMILY " smpram.qsf | cut -d\"  -f2`
set DEV  = `grep " DEVICE " smpram.qsf | cut -d" " -f4`
set TTL1 = '                                       Fmax-MHz 0.9v     Combinational ALUT usage for logic                               LABs           I/O Pins              BRAM Bits Utiliz.             \n'
set TTL2 = '              RAM   Data  Write Read  ------------- ----------------------------------------- Route  Total  Total  ----------------- -------------- BRAM MLAB -----------------      Runtime\n'
set TTL3 = 'Arch.  Bypass Depth Width Ports Ports T = 0c T= 85c Total  7-LUTs 6-LUTs 5-LUTs 4-LUTs 3-LUTs ALUTs  Reg.   ALMs   Total Logic Mem.  Tot. Clk  Ded. M20K Bits Utilized Occupied DSPs Minutes\n'
set SEPR = '====== ====== ===== ===== ===== ===== ====== ====== ====== ====== ====== ====== ====== ====== ====== ====== ====== ===== ===== ===== ==== ==== ==== ==== ==== ======== ======== ==== =======\n'
set FRMT = (`echo $SEPR| tr " " "\n" | perl -nle '$a= length; print "%-${a}s"' | tr "\n" " "`)
if !(-f syn.res) then
  printf "$FML $DEV\n\n$TTL1$TTL2$TTL3$SEPR" >! syn.res
endif

#initialize result values
set val  = (`repeat 29 echo "N/A"`)

# create log directoy
if !(-d log) mkdir log

# operate on all different RAM parameters
foreach CURRD ($RDLST)
  foreach CURDW ($DWLST)
    foreach CURNW ($NWLST)
      # extract normal and extended parameters
      set CURNWN = `echo $CURNW|cut -d"-" -f1`
      set CURNWE = `echo $CURNW|cut -d"-" -f2`
      if (CURNWE == "") set CURNWE = $CURNWN
      foreach CURNR ($NRLST)
        # extract normal and extended parameters
        set CURNRN = `echo $CURNR|cut -d"-" -f1`
        set CURNRE = `echo $CURNR|cut -d"-" -f2`
        if (CURNRE == "") set CURNRE = $CURNRN
        foreach CURAR ($ARLST)
          foreach CURBY ($BYLST)

            @ FlowOprCnt++
            set curRunStartTime      = `date +%T`
            set curRunStartTimeStamp = `date +%s`
            set RUNNAME = "${CURAR}-${CURBY}_${CURRD}x${CURDW}_${CURNW}W${CURNR}R"

            printf '\x1b[%i;3%im' 7 2
            printf "\n== Starting Synthesis  (${FlowOprCnt}/${FlowOprNum}) @${curRunStartTime}: [Depth:${CURRD}; Width:${CURDW}; Writes:${CURNW}; Reads:${CURNR}; Architicture:${CURAR}]\n"
            printf '\x1b[0m'

            # create configuration file base on architectural
            printf '// Multi-ported RAM Configuration File\n'                                                >! config.vh
            printf '// Generated by flow manager before logic synthesis\n'                                   >> config.vh
            printf '`define ARCH "%s"\t// Architecture: REG, XOR, LVTREG, LVTBIN, LVT1HT\n'          $CURAR  >> config.vh
            printf '`define BYPS "%s"\t// Bypass: NON, WAW, RAW, RDW\n'                            $CURBY  >> config.vh
            printf '`define MEMD %s\t\t// Memory Depth (lines) \n'                                   $CURRD  >> config.vh
            printf '`define DATW %s\t\t\t// Data Width (bits) \n'                                      $CURDW  >> config.vh
            printf '`define nWPN %s\t\t\t// Number of writing ports - normal   (   0< nWPN        )\n' $CURNWN >> config.vh
            printf '`define nRPN %s\t\t\t// Number of reading ports - normal   (   0< nRPN        )\n' $CURNRN >> config.vh
            printf '`define nWPE %s\t\t\t// Number of writing ports - extended (   0<=nWPE<=nWPN  )\n' $CURNWE >> config.vh
            printf '`define nRPE %s\t\t\t// Number of reading ports - extended (nRPN<=nRPE<=2*nRPN)\n' $CURNRE >> config.vh

            # clean previous report files before run
            foreach fileName (smpram.asm.rpt smpram.eda.rpt smpram.fit.rpt smpram.flow.rpt smpram.map.rpt smpram.sta.rpt smpram.done smpram.fit.summary smpram.map.summary smpram.sta.summary smpram.pin smpram.sof)
              if (-e $fileName) \rm -rf $fileName
            end

            # clean previous values before run
            set val  = (`repeat 29 echo "N/A"`)

            # run current synthesis
            quartus_map --64bit --read_settings_files=on --write_settings_files=off smpram -c smpram  | tee log/${RUNNAME}.map.log
            quartus_cdb --64bit --merge  smpram -c smpram                                             | tee log/${RUNNAME}.cdb.log
            quartus_fit --64bit --read_settings_files=off --write_settings_files=off smpram -c smpram | tee log/${RUNNAME}.fit.log
            quartus_sta --64bit smpram -c smpram                                                      | tee log/${RUNNAME}.sta.log

            # calculate runtime and generate a report / per run
            set curRunFinishTime      = `date +%T`
            set curRunFinishTimeStamp = `date +%s`
            @   curRunTimeDiff        = $curRunFinishTimeStamp - $curRunStartTimeStamp
            set curRuntimeMin         =   `echo "scale=2;$curRunTimeDiff/60"|bc`

            # collect data
            set val[1]  = $CURAR
            set val[2]  = $CURBY
            set val[3]  = $CURRD
            set val[4]  = $CURDW
            set val[5]  = $CURNW
            set val[6]  = $CURNR
            if (-f smpram.sta.rpt) then
              set val[7]  = `grep -a4 "Slow 900mV 0C Model Fmax Summary"  smpram.sta.rpt | tail -1 | cut -d" " -f2 | tr -d " \n"`; 
              set val[8]  = `grep -a4 "Slow 900mV 85C Model Fmax Summary" smpram.sta.rpt | tail -1 | cut -d" " -f2 | tr -d " \n"`
            endif

            if (-f smpram.fit.rpt) then
              grep -A92 "; Fitter Resource Usage Summary" smpram.fit.rpt >! __fit_rpt__.tmp
              set val[9]  = `grep "ALUT usage for logic"        __fit_rpt__.tmp | cut -d";" -f3 | cut -d"/" -f1| tr -d ", "`
              set val[10] = `grep "7 input"                     __fit_rpt__.tmp | cut -d";" -f3 | cut -d"/" -f1| tr -d ", "`
              set val[11] = `grep "6 input"                     __fit_rpt__.tmp | cut -d";" -f3 | cut -d"/" -f1| tr -d ", "`
              set val[12] = `grep "5 input"                     __fit_rpt__.tmp | cut -d";" -f3 | cut -d"/" -f1| tr -d ", "`
              set val[13] = `grep "4 input"                     __fit_rpt__.tmp | cut -d";" -f3 | cut -d"/" -f1| tr -d ", "`
              set val[14] = `grep "<=3 input"                   __fit_rpt__.tmp | cut -d";" -f3 | cut -d"/" -f1| tr -d ", "`
              set val[15] = `grep "ALUT usage for route"        __fit_rpt__.tmp | cut -d";" -f3 | cut -d"/" -f1| tr -d ", "`
              set val[16] = `grep "Dedicated logic registers"   __fit_rpt__.tmp | cut -d";" -f3 | cut -d"/" -f1| tr -d ", "`
              set val[17] = `grep "ALMs needed \["              __fit_rpt__.tmp | cut -d";" -f3 | cut -d"/" -f1| tr -d ", "`
              set val[18] = `grep "Total LABs"                  __fit_rpt__.tmp | cut -d";" -f3 | cut -d"/" -f1| tr -d ", "`
              set val[19] = `grep "Logic LABs"                  __fit_rpt__.tmp | cut -d";" -f3 | cut -d"/" -f1| tr -d ", "`
              set val[20] = `grep "Memory LABs"                 __fit_rpt__.tmp | cut -d";" -f3 | cut -d"/" -f1| tr -d ", "`
              set val[21] = `grep "I/O pins"                    __fit_rpt__.tmp | cut -d";" -f3 | cut -d"/" -f1| tr -d ", "`
              set val[22] = `grep "Clock pins"                  __fit_rpt__.tmp | cut -d";" -f3 | cut -d"/" -f1| tr -d ", "`
              set val[23] = `grep "Dedicated input"             __fit_rpt__.tmp | cut -d";" -f3 | cut -d"/" -f1| tr -d ", "`
              set val[24] = `grep "M20K"                        __fit_rpt__.tmp | cut -d";" -f3 | cut -d"/" -f1| tr -d ", "`
              set val[25] = `grep "MLAB"                        __fit_rpt__.tmp | cut -d";" -f3 | cut -d"/" -f1| tr -d ", "`
              set val[26] = `grep "block memory bits"           __fit_rpt__.tmp | cut -d";" -f3 | cut -d"/" -f1| tr -d ", "`
              set val[27] = `grep "block memory implementation" __fit_rpt__.tmp | cut -d";" -f3 | cut -d"/" -f1| tr -d ", "`
              set val[28] = `grep "DSP"                         __fit_rpt__.tmp | cut -d";" -f3 | cut -d"/" -f1| tr -d ", "`
              set val[29] = $curRuntimeMin
              \rm -rf __fit_rpt__.tmp
            endif
            foreach i (`seq 29`)
              if ( $val[$i] == "" ) set val[$i] = "N/A"
            end

            # print to report
            printf "$FRMT\n" $val >> syn.res

            # move log files into log directory
            foreach fileName (smpram.asm.rpt smpram.eda.rpt smpram.fit.rpt smpram.flow.rpt smpram.map.rpt smpram.sta.rpt smpram.done smpram.fit.summary smpram.map.summary smpram.sta.summary smpram.pin)
              if (-f $fileName) mv $fileName "log/${RUNNAME}.`echo $fileName | cut -d. -f2-`"
            end

            printf '\x1b[%i;3%im' 7 2
            printf "== Synthesis (${FlowOprCnt}/${FlowOprNum}) Completed after ${curRuntimeMin} minutes: [Depth:${CURRD}; Width:${CURDW}; Writes:${CURNW}; Reads:${CURNR}; Architicture:${CURAR}]\n"
            printf '\x1b[0m'

          end
        end
      end
    end
  end
end

# clean unrequired files / after run
foreach fileName (db/ hc_output/ incremental_db/ smpram.asm.rpt smpram.eda.rpt smpram.merge.rpt smpram.fit.rpt smpram.flow.rpt smpram.map.rpt smpram.sta.rpt smpram.done smpram.merge.summary smpram.fit.summary smpram.fit.smsg smpram.map.summary smpram.sta.summary smpram.pin smpram.sof smpram.map.smsg smpram_assignment_defaults.qdf)
  if (-e $fileName) \rm -rf $fileName
end

goto scriptEnd

# error message
errorMessage:
printf '\x1b[%i;3%im' 1 1
printf 'USAGE:                                                                        \n'
printf '   ./syn <Architecture List> <Bypass List> <Depth List> <Width List>   \\     \n'
printf '         <#Write Ports List> <#Read Ports List>                               \n'
printf '- Use a comma delimited list.                                                 \n'
printf '  - No spaces.                                                                \n'
printf '  - May be surrounded by any brackets (), [], {}, or <>.                      \n'
printf '- RAM depth and data width are positive integers.                             \n'
printf '- Numbers of read and write ports are pairs of normal-extended port numbers   \n'
printf '  delimited with hyphen "-".                                                  \n'
printf '  - numbers of normal read/write ports are positive integers.                 \n'
printf '  - 0                   <= # Extended write ports <=     # normal write ports \n'
printf '  - # normal read ports <= # Extended read  ports <= 2 * # noraml read  ports \n'
printf '- Architecture is one of: REG, XOR, LVTREG, LVTBIN, or LVT1HT.                \n'
printf '  - REG   : Register-based multi-ported RAM                                   \n'
printf '  - XOR   : XOR-based multi-ported RAM                                        \n'
printf '  - LVTREG: Register-based LVT multi-ported RAM                               \n'
printf '  - LVTBIN: Binary-coded I-LVT-based multi-ported RAM                         \n'
printf '  - LVT1HT: Onehot-coded I-LVT-based multi-ported RAM                         \n'
printf '-Bypass list is one of: NON, WAW, RAW, or RDW.                                \n'
printf '  - WAW: Allow Write-After-Write (need to bypass feedback RAM)                \n'
printf '  - RAW: new data for Read-after-Write (need to bypass output RAM)            \n'
printf '  - RDW: new data for Read-During-Write                                       \n'
printf 'EXAMPLES:                                                                     \n'
printf './syn XOR NON 1024 32 3-2 2-4                                                 \n'
printf '   Synthesis a XOR-based RAM with no bypassing; 1K lines RAM; 32 bits width;  \n'
printf '   3 normal / 2 extended write & 2 normal / 4 extended read ports.            \n'
printf './syn LVTBIN,LVT1HT RAW,RDW 512,1024 8,16,32 2,3,4 1,2,3,4                    \n'
printf '   Synthesis LVTBIN and LVT1HT RAMs with new data RAW and RDW bypassing; 512  \n'
printf '   and 1024 lines; 8,16, and 32 data width; 2,3, and 4 write ports; 1,2,3, and\n'
printf '   4 read ports. Total of 144 RAM combinations.                               \n'
printf 'The following files and directories will be created after compilation:        \n'
printf '  - syn.res : A list of results, each run in a separate line, including:      \n'
printf '              frequency, resources usage, and runtime                         \n'
printf '  - log/    : Altera''s logs and reports                                      \n'
printf '\x1b[0m'

scriptEnd:

